﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace SettlersEngine
{
    public unsafe class ImagePixelLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
    {
        private static byte[] buffer = new byte[1024];
        private static byte[] tmpBuffer = new byte[1024];
        private Bitmap bitmap;
        private BitmapData data;
        public Boolean IsCopy { get; private set; }
        public Int64 Checksum { get; private set; }
        public int* Pixels { get; private set; }

        public int Width { get { return bitmap.Width; } }
        public int Height { get { return bitmap.Height; } }

        public ImagePixelLock(Bitmap inSource)
            : this(inSource, new System.Drawing.Rectangle(0, 0, inSource.Width, inSource.Height), false)
        {
        }

        public ImagePixelLock(Bitmap inSource, Boolean inCreateCopy)
            : this(inSource, new System.Drawing.Rectangle(0, 0, inSource.Width, inSource.Height), inCreateCopy)
        {
        }

        public ImagePixelLock(Bitmap inSource, System.Drawing.Rectangle inLockRegion)
            : this(inSource, inLockRegion, false)
        {
        }

        public ImagePixelLock(Bitmap inSource, System.Drawing.Rectangle inLockRegion, Boolean inCreateCopy)
        {
            if (inSource.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
                throw new ArgumentException("Given bitmap has an unsupported pixel format.");

            IsCopy = inCreateCopy;

            if (inCreateCopy)
                bitmap = (Bitmap)inSource.Clone();
            else
                bitmap = inSource;

            data = bitmap.LockBits(inLockRegion, ImageLockMode.ReadWrite, inSource.PixelFormat);
            Pixels = (int*)data.Scan0.ToPointer();

            // compute checksum from pixeldata
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            int* ptr = (int*)data.Scan0.ToPointer();

            for (int i = 0, byteCount = Width * Height * 4; i < byteCount; i += buffer.Length)
            {
                int count = Math.Min(buffer.Length, byteCount - i);

                System.Runtime.InteropServices.Marshal.Copy((IntPtr)ptr, buffer, 0, count);
                md5.TransformBlock(buffer, 0, count, tmpBuffer, 0);

                ptr += count / 4;
            }

            md5.TransformFinalBlock(new byte[0], 0, 0);

            byte[] checksum = md5.Hash;

            for (int i = 0; i < 8; i++)
            {
                Checksum |= (((Int64)checksum[i]) << (i * 8));
            }
        }

        public override int GetHashCode()
        {
            return unchecked((Int32)Checksum);
        }

        ~ImagePixelLock()
        {
            Dispose();
        }

        public void Dispose()
        {
            try
            {
                if ((data != null) && (bitmap != null))
                    bitmap.UnlockBits(data);

                if (IsCopy && (bitmap != null))
                    bitmap.Dispose();
            }
            catch
            {
                // bitmap might be already disposed even if we got a valid pixellock
            }

            data = null;
            bitmap = null;
            Pixels = (int*)0;
        }
    }

    internal class WPFImaging
    {
        internal static BitmapSource LoadImage(String inFilename)
        {
            FileStream source = new FileStream(inFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
            MemoryStream cache = new MemoryStream();

            using (source)
            {
                source.CopyTo(cache);

                cache.Position = 0;

                return BitmapDecoder.Create(cache, BitmapCreateOptions.None, BitmapCacheOption.Default).Frames[0];
            }
        }

        internal static RenderTargetBitmap ComponentScreenshot(FrameworkElement inComponent)
        {
            var bounds = VisualTreeHelper.GetDescendantBounds(inComponent);
            System.Windows.Size size = bounds.Size;

            RenderTargetBitmap bitmap = new RenderTargetBitmap((int)inComponent.ActualWidth, (int)inComponent.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            DrawingVisual drawingvisual = new DrawingVisual();

            using (DrawingContext context = drawingvisual.RenderOpen())
            {
                context.DrawRectangle(new VisualBrush(inComponent), null, new Rect(0, 0, (int)inComponent.ActualWidth, (int)inComponent.ActualHeight));
                context.Close();
            }

            try
            {
                bitmap.Render(drawingvisual);
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.ToString());
            }

            return bitmap;
        }

        internal static MemoryStream DrawingImageToStream(System.Drawing.Image inImage)
        {
            MemoryStream stream = new MemoryStream();

            inImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png);

            stream.Position = 0;

            return stream;
        }

        internal static MemoryStream BitmapSourceToStream(BitmapSource inSource)
        {
            PngBitmapEncoder png = new PngBitmapEncoder();
            BitmapFrame tmpImage;

            // convert WPF component to Drawing.Bitmap
            tmpImage = BitmapFrame.Create(inSource);
            png.Frames.Add(tmpImage);

            MemoryStream stm = new MemoryStream();

            png.Save(stm);

            stm.Position = 0;

            return stm;
        }
    }
}
